<?php

namespace app\api\controller;

use think\Controller;
use think\Db;
use think\Exception;

class Importdata extends Controller
{
    // 批处理数量
    const BATCH_SIZE = 100;
    // 文件类型常量
    const TYPE_PDF = 'pdf';
    const TYPE_TXT = 'txt';
    const TYPE_XML = 'xml';

    public function index()
    {
        try {
            // 获取可用任务
            $task = $this->getAvailableTask();
            if (empty($task)) {
                return $this->sleepAndReturn('没有任务');
            }

            // 获取队列列表
            $zipQueueList = $this->getQueueList($task['id']);
            if (empty($zipQueueList)) {
                $this->completeTask($task['id']);
                return $this->sleepAndReturn('没有任务');
            }

            // 处理队列
            $this->processQueues($task, $zipQueueList);

            return json(['code' => 200, 'message' => '处理完成']);
        } catch (Exception $e) {
            // 记录错误日志
            trace("导入错误: " . $e->getMessage(), 'error');
            return json(['code' => 500, 'message' => '导入错误: ' . $e->getMessage()]);
        }
    }

    /**
     * 获取可用任务
     * 
     * @return array|null 任务信息
     */
    private function getAvailableTask()
    {
        $map = [
            'status' => 0,
            'is_delete' => 0
        ];
        return Db::table('zstp_zip_task')->where($map)->order('id asc')->find();
    }

    /**
     * 获取队列列表
     * 
     * @param int $taskId 任务ID
     * @return array 队列列表
     */
    private function getQueueList($taskId)
    {
        $mapZip = [
            'task_id' => $taskId,
            'is_delete' => 0,
            'status' => 0 // 只处理未处理的
        ];
        return Db::table('zstp_zip_queue')
            ->where($mapZip)
            ->order('id asc')
            ->limit(self::BATCH_SIZE)
            ->select();
    }

    /**
     * 标记任务完成
     * 
     * @param int $taskId 任务ID
     */
    private function completeTask($taskId)
    {
        Db::table('zstp_zip_task')->where('id', $taskId)->update([
            'status' => 1,
            'update_time' => date('Y-m-d H:i:s')
        ]);
    }

    /**
     * 休眠并返回消息
     * 
     * @param string $message 返回消息
     * @return \think\response\Json 
     */
    private function sleepAndReturn($message)
    {
        sleep(10);
        return json(['code' => 400, 'message' => $message]);
    }

    /**
     * 处理队列
     * 
     * @param array $task 任务信息
     * @param array $zipQueueList 队列列表
     */
    public function processQueues($task, $zipQueueList)
    {
        $path = config('path') . "data/" . $task['id'] . "/";
    

        foreach ($zipQueueList as $value) {
            try {
                //更新队列正在处理 
                Db::table('zstp_zip_queue')->where('id', $value['id'])->update([
                    'status' => 2, // 正在处理状态
                    'update_time' => date('Y-m-d H:i:s')
                ]); 

                $processed = $this->processZipFile($task, $value, $path);
              
                if($processed){
                    return [
                        'code' => 200,
                        'message' => '处理成功'
                    ];
                }else{
                    return [
                        'code' => 400,
                        'message' => '处理失败'
                    ];
                }

            } catch (Exception $e) {
        
                trace("处理ZIP文件失败 (ID:{$value['id']}): " . $e->getMessage(), 'error');
                
                // 标记为处理失败
                Db::table('zstp_zip_queue')->where('id', $value['id'])->update([
                    'status' => 3, // 失败状态
                    'error_msg' => $e->getMessage(),
                    'update_time' => date('Y-m-d H:i:s')
                ]);
            }
        }



    }




    /**
     * 处理单个ZIP文件
     * 
     * @param array $task 任务信息
     * @param array $queueItem 队列项
     * @param string $basePath 基础路径
     * @return bool 是否成功处理
     */
    private function processZipFile($task, $queueItem, $basePath)
    {
        // 准备解压路径
        $pathParts = explode('/', $queueItem['path']);
        $year = isset($pathParts[count($pathParts) - 2]) ? $pathParts[count($pathParts) - 2] : '';
        $folderName = basename($queueItem['path'], '.zip');
        $filePath = $basePath . $year . '/' . $folderName . '/';
        
        // 解压文件并获取所有解压后的文件路径
        $extractedFiles = $this->unzipFile($queueItem['path'], $filePath);
        if (empty($extractedFiles)) {
            throw new Exception("解压文件失败或压缩包为空");
        }
        
        // 提取文件内容
        $dataInsert = $this->extractFileContents($extractedFiles);
        
        // 检查是否有必要的字段
        if (empty($dataInsert) || (!isset($dataInsert['dh']) && !isset($dataInsert['tm']))) {
            throw new Exception("未提取到必要的档案信息");
        }
        
        // 添加任务相关信息
        $dataInsert['impor_task_id'] = $task['id'];
        $dataInsert['impor_queue_id'] = $queueItem['id'];
    
    
        // 插入数据库
        $insertId = Db::table('zstp_article')->insertGetId($dataInsert);
        
        if ($insertId) {
            // 更新队列状态
            Db::table('zstp_zip_queue')->where('id', $queueItem['id'])->update([
                'status' => 1,
                'update_time' => date('Y-m-d H:i:s')
            ]);
            return true;
        }
        
        return false;
    }
    
    /**
     * 提取文件内容
     * 
     * @param array $extractedFiles 解压的文件列表
     * @return array 提取的数据
     */
    private function extractFileContents($extractedFiles)
    {
        $dataInsert = [];
        
        // 获取PDF文件路径
        $pdfPath = $this->getFileByType($extractedFiles, self::TYPE_PDF);
        if (!empty($pdfPath)) {
            $dataInsert['double_layer_pdf'] = $pdfPath;
        }
        
        // 获取TXT内容
        $content = $this->getFileByType($extractedFiles, self::TYPE_TXT);
        if (!empty($content)) {
            $dataInsert['content'] = $content;
        }
        
        // 获取XML数据
        $xmlArray = $this->getFileByType($extractedFiles, self::TYPE_XML);
        if (!empty($xmlArray) && is_array($xmlArray)) {
            $dataInsert = array_merge($dataInsert, $xmlArray);
        }
        
        return $dataInsert;
    }

    /**
     * 解压ZIP文件并返回解压后的所有文件路径
     * 
     * @param string $zipFile ZIP文件路径
     * @param string $path 解压目标路径
     * @return array 解压后的文件路径数组
     * @throws Exception 如果解压失败
     */
    public function unzipFile($zipFile, $path)
    {
        // 确保目标目录存在
        if (!is_dir($path)) {
            if (!mkdir($path, 0777, true)) {
                throw new Exception("创建目录失败: {$path}");
            }
        }
        
        $extractedFiles = [];
        $zip = new \ZipArchive();
        
        $openResult = $zip->open($zipFile);
        if ($openResult !== true) {
            throw new Exception("打开ZIP文件失败，错误码: {$openResult}");
        }
        
        // 获取压缩包中的文件数量
        $numFiles = $zip->numFiles;
        
        // 解压文件
        $res = $zip->extractTo($path);
        
        if (!$res) {
            $zip->close();
            throw new Exception("解压文件失败");
        }
        
        // 遍历压缩包中的所有文件
        for ($i = 0; $i < $numFiles; $i++) {
            $filename = $zip->getNameIndex($i);
            $fullPath = $path . $filename;
            
            // 忽略目录项，只添加文件
            if (!is_dir($fullPath) && file_exists($fullPath)) {
                $extractedFiles[] = $fullPath;
            }
        }
        
        $zip->close();
        
        return $extractedFiles;
    }

    /**
     * 根据文件类型获取对应的文件内容或路径
     * 
     * @param array $files 文件路径数组
     * @param string $fileType 文件类型（pdf, txt, xml等）
     * @return mixed 文件内容、路径或数组
     */
    public function getFileByType($files, $fileType)
    {
        // 确保文件类型是小写的，去掉可能的点号
        $fileType = strtolower(ltrim($fileType, '.'));
        
        foreach ($files as $file) {
            // 获取文件扩展名并转换为小写
            $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
            
            // 如果扩展名匹配，处理文件
            if ($extension === $fileType) {
                switch ($fileType) {
                    case self::TYPE_PDF:
                        return $this->getPdfUrl($file);
                        
                    case self::TYPE_TXT:
                        // 读取并返回TXT内容
                        $content = @file_get_contents($file);
                        if ($content === false) {
                            trace("读取TXT文件失败: {$file}", 'error');
                            return null;
                        }
                        return $content;
                        
                    case self::TYPE_XML:
                        return $this->parseXmlFile($file);
                        
                    default:
                        return $file;
                }
            }
        }
        
        return null;
    }

    /**
     * 解析XML文件
     * 
     * @param string $file XML文件路径
     * @return array 解析后的数组
     */
    private function parseXmlFile($file)
    {
        // 读取XML文件内容
        $xml = @file_get_contents($file);
        if ($xml === false) {
            trace("读取XML文件失败: {$file}", 'error');
            return [];
        }
        
        // 将XML转换为一维数组，以SourceFieldName为键、value为值
        $result = [];
        
        // 加载XML，使用libxml错误处理
        libxml_use_internal_errors(true);
        $xmlObj = simplexml_load_string($xml);
        
        if ($xmlObj === false) {
            $errors = libxml_get_errors();
            foreach ($errors as $error) {
                trace("XML解析错误: " . $error->message, 'error');
            }
            libxml_clear_errors();
            return [];
        }
        
        // 尝试所有可能的XML结构
        $this->parseXmlStructures($xmlObj, $result);
        
        return $result;
    }
    
    /**
     * 解析所有可能的XML结构
     * 
     * @param SimpleXMLElement $xmlObj
     * @param array &$result
     */
    private function parseXmlStructures($xmlObj, &$result)
    {
        // 检查是否有row元素，每个row包含SourceFieldName和value属性
        if (isset($xmlObj->row)) {
            $this->extractRowAttributes($xmlObj->row, $result);
        }
        
        // 遍历Root/row结构
        if (empty($result) && isset($xmlObj->Root) && isset($xmlObj->Root->row)) {
            $this->extractRowAttributes($xmlObj->Root->row, $result);
        }
        
        // 直接检查根元素下的row
        if (empty($result)) {
            foreach ($xmlObj->children() as $child) {
                if ($child->getName() == 'row') {
                    $this->extractAttributes($child, $result);
                }
            }
        }
    }
    
    /**
     * 从row元素集合中提取属性
     * 
     * @param SimpleXMLElement $rows
     * @param array &$result
     */
    private function extractRowAttributes($rows, &$result)
    {
        foreach ($rows as $row) {
            $this->extractAttributes($row, $result);
        }
    }
    
    /**
     * 从元素中提取SourceFieldName和value属性
     * 
     * @param SimpleXMLElement $element
     * @param array &$result
     */
    private function extractAttributes($element, &$result)
    {
        $attributes = $element->attributes();
        if (isset($attributes['SourceFieldName']) && isset($attributes['value'])) {
            $fieldName = (string)$attributes['SourceFieldName'];
            $fieldValue = (string)$attributes['value'];
            if (!empty($fieldName)) {
                $result[$fieldName] = $fieldValue;
            }
        }
    }

    /**
     * 获取PDF URL
     * 
     * @param string $file PDF文件路径
     * @return string 转换后的URL路径
     */
    public function getPdfUrl($file)
    {
        $path = config('path');
        $str = str_replace($path, '', $file);
        $str = str_replace('\\', '/', $str);
        return $str;
    }
}
